/*
 *    Copyright (c) 2018-2025, boss All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the boss developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: boss
 */
package com.hopes.bizz.poll.monitor.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hopes.bizz.common.core.util.StringUtils;
import com.hopes.bizz.poll.alarm.entity.PollAlarmEntity;
import com.hopes.bizz.poll.alarm.entity.PollAlarmExceptionEntity;
import com.hopes.bizz.poll.alarm.entity.PollAlarmNetEntity;
import com.hopes.bizz.poll.alarm.service.PollAlarmExceptionService;
import com.hopes.bizz.poll.alarm.service.PollAlarmNetService;
import com.hopes.bizz.poll.alarm.service.PollAlarmService;
import com.hopes.bizz.poll.analy.vo.MonitorSiteAlarmVO;
import com.hopes.bizz.poll.analy.vo.MonitorSiteStatusVO;
import com.hopes.bizz.poll.common.constant.AlarmConstant;
import com.hopes.bizz.poll.common.constant.enums.PollModDictEnum;
import com.hopes.bizz.poll.common.constant.enums.PollSysDictEnum;
import com.hopes.bizz.poll.monitor.dto.PollMonitorSiteExcelDTO;
import com.hopes.bizz.poll.monitor.entity.PollMonitorFactorEntity;
import com.hopes.bizz.poll.monitor.entity.PollMonitorSiteEntity;
import com.hopes.bizz.poll.monitor.entity.PollMonitorTypeEntity;
import com.hopes.bizz.poll.monitor.mapper.PollMonitorSiteMapper;
import com.hopes.bizz.poll.monitor.service.PollMonitorFactorService;
import com.hopes.bizz.poll.monitor.service.PollMonitorSiteService;
import com.hopes.bizz.poll.monitor.service.PollMonitorTypeService;
import com.hopes.bizz.poll.monitor.vo.PollMonitorSiteByMainTypeVO;
import com.hopes.bizz.poll.monitor.vo.PollMonitorSiteExcelVO;
import com.hopes.bizz.poll.monitor.vo.PollMonitorSiteVo;
import com.hopes.bizz.poll.gis.vo.GisAir;
import com.hopes.bizz.poll.gis.vo.GisWater;
import com.hopes.boss.admin.api.resolver.DictResolver;
import com.hopes.boss.admin.api.resolver.enums.AdminDictEnum;
import com.hopes.boss.admin.api.resolver.enums.SysDictEnum;
import com.hopes.boss.common.core.util.ClassUtils;
import com.hopes.boss.common.entity.ValidateImportBO;
import com.hopes.boss.common.excel.vo.ErrorMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 监控点信息
 *
 * @author pig
 * @date 2023-05-19 13:28:48
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PollMonitorSiteServiceImpl extends ServiceImpl<PollMonitorSiteMapper, PollMonitorSiteEntity> implements PollMonitorSiteService {

    private final String CODE_ERROR_MSG = "站点编码不允许重复!";

    private final PollMonitorFactorService pollMonitorFactorService;

    private final PollAlarmService pollAlarmService;

    private final PollAlarmNetService pollAlarmNetService;

    private final PollAlarmExceptionService pollAlarmExceptionService;

    private final PollMonitorTypeService pollMonitorTypeService;

    @SuppressWarnings("unchecked")
    public Page<PollMonitorSiteVo> queryPage(Page page, Wrapper<PollMonitorSiteEntity> query) {
        final Page<PollMonitorSiteEntity> selectPage = baseMapper.selectPage(page, query);
        final List<PollMonitorSiteVo> collect = selectPage.getRecords().stream().map(item -> {
            final Long monitorSiteId = item.getId();
            final PollMonitorFactorEntity param = new PollMonitorFactorEntity();
            param.setMonitorSiteId(monitorSiteId);
            final List<PollMonitorFactorEntity> monitorFactorEntityList = pollMonitorFactorService.list(Wrappers.query(param));

            final PollMonitorSiteVo pollMonitorSiteVo = new PollMonitorSiteVo();
            BeanUtils.copyProperties(item, pollMonitorSiteVo);
            pollMonitorSiteVo.setPollMonitorFactorEntityList(monitorFactorEntityList);
            if (StringUtils.isNotBlank(item.getVideoIds())) {
                pollMonitorSiteVo.setVideoAssociation(true);
            }
            return pollMonitorSiteVo;
        }).collect(Collectors.toList());

        // 构建 vo page 对象
        final Page<PollMonitorSiteVo> res = new Page<>(page.getCurrent(), page.getSize(), selectPage.getTotal());
        res.setRecords(collect);
        return res;
    }

    @Override
    public List<MonitorSiteStatusVO> statisticMonitorSiteStatus() {
        LambdaQueryWrapper<PollMonitorSiteEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(PollMonitorSiteEntity::getIsUse, true);
        Map<String, List<PollMonitorSiteEntity>> collect = this.list(wrapper).stream().collect(Collectors.groupingBy(PollMonitorSiteEntity::getMonitorType));
        List<MonitorSiteStatusVO> result = new ArrayList<>();

        for (Map.Entry<String, List<PollMonitorSiteEntity>> stringListEntry : collect.entrySet()) {
            MonitorSiteStatusVO vo = new MonitorSiteStatusVO();
            vo.setMonitorSiteType(stringListEntry.getKey());

            List<MonitorSiteAlarmVO> alarmStatusList = new ArrayList<>();
            for (PollMonitorSiteEntity entity : stringListEntry.getValue()) {
                MonitorSiteAlarmVO alarmStatus = new MonitorSiteAlarmVO();
                BeanUtils.copyProperties(entity, alarmStatus);
                alarmStatusList.add(alarmStatus);
            }

            vo.setMonitorSiteList(alarmStatusList);
            result.add(vo);
        }

        return result;
    }

    @Override
    public Boolean monitorSiteStatusJobHandler() {
        //
        LambdaQueryWrapper<PollMonitorSiteEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(PollMonitorSiteEntity::getIsUse, true);
        wrapper.eq(PollMonitorSiteEntity::getIsOnline, true);
        List<PollMonitorSiteEntity> monitorSites = this.list(wrapper);

        for (PollMonitorSiteEntity monitorSite : monitorSites) {
            LambdaQueryWrapper<PollAlarmNetEntity> netWrapper = Wrappers.lambdaQuery();
            netWrapper.eq(PollAlarmNetEntity::getMonitorSiteId, monitorSite.getId());
            netWrapper.eq(PollAlarmNetEntity::getAlarmStatus, AlarmConstant.ALARM_STATUS_NOT);
            long netCount = pollAlarmNetService.count(netWrapper);
            monitorSite.setNetFlag(netCount > 0);

            LambdaQueryWrapper<PollAlarmEntity> alarmWrapper = Wrappers.lambdaQuery();
            alarmWrapper.eq(PollAlarmEntity::getMonitorSiteId, monitorSite.getId());
            alarmWrapper.eq(PollAlarmEntity::getAlarmStatus, AlarmConstant.ALARM_STATUS_NOT);
            long alarmCount = pollAlarmService.count(alarmWrapper);
            monitorSite.setAlarmFlag(alarmCount > 0);

            LambdaQueryWrapper<PollAlarmExceptionEntity> alarmExceptionWrapper = Wrappers.lambdaQuery();
            alarmExceptionWrapper.eq(PollAlarmExceptionEntity::getMonitorSiteId, monitorSite.getId());
            alarmExceptionWrapper.eq(PollAlarmExceptionEntity::getAlarmStatus, AlarmConstant.ALARM_STATUS_NOT);
            long alarmExceptionCount = pollAlarmExceptionService.count(alarmExceptionWrapper);
            monitorSite.setExceptionFlag(alarmExceptionCount > 0);
        }

        this.updateBatchById(monitorSites);
        return true;
    }

    @Override
    public ValidateImportBO<PollMonitorSiteExcelDTO> validateImport(List<PollMonitorSiteExcelDTO> excelDTOList) {
        ValidateImportBO<PollMonitorSiteExcelDTO> bo = new ValidateImportBO<>();
        List<ErrorMessage> errorMessageList = new ArrayList<>();
        List<PollMonitorSiteExcelDTO> saveList = new ArrayList<>();
        int successNum = 0;
        int failedNum = 0;

        final List<PollMonitorSiteEntity> list = this.list();
        for (PollMonitorSiteExcelDTO excelDTO : excelDTOList) {
            final Field[] fields = excelDTO.getClass().getFields();
            // 1、验证非空字段
            final Set<String> errorMsg = requiredNonNull(excelDTO, fields);
            // 2、验证站点编码是否存在
            final boolean exist = list.stream().anyMatch(item -> excelDTO.getCode().equals(item.getCode()));
            if (exist) {
                errorMsg.add("站点编码" + excelDTO.getCode() + "不允许重复");
            }
            if (errorMsg.isEmpty()) {
                saveList.add(excelDTO);
                successNum++;
            } else {
                errorMessageList.add(new ErrorMessage(excelDTO.getLineNum(), errorMsg));
                failedNum++;
            }
        }

        bo.setErrorMessages(errorMessageList);
        bo.setSaveList(saveList);
        bo.setSuccessNum(successNum);
        bo.setFailedNum(failedNum);
        return bo;
    }

    /**
     * 验证非空字段
     *
     * @param excelDTO 对象
     * @param fields   所属属性
     * @return 验证结果
     */
    private Set<String> requiredNonNull(PollMonitorSiteExcelDTO excelDTO, Field[] fields) {
        final HashSet<String> result = new HashSet<>();
        for (Field field : fields) {
            final NotNull annotation = field.getAnnotation(NotNull.class);
            if (null == annotation) {
                continue;
            }
            try {
                final Object propertyValue = ClassUtils.getPropertyValue(excelDTO, field.getName());
                if (null == propertyValue || propertyValue.toString().trim().equals("")) {
                    result.add(annotation.message());
                }
            } catch (Exception e) {
                log.error(" class: {}, field:{}, get value error !", excelDTO.getClass(), field.getName());
            }
        }
        return result;
    }

    @Override
    public PollMonitorSiteEntity importDtoToEntity(PollMonitorSiteExcelDTO excelDto) {
        PollMonitorSiteEntity entity = new PollMonitorSiteEntity();
        BeanUtils.copyProperties(excelDto, entity);
        entity.setIsUse(StringUtils.getUseFlag(excelDto.getIsUse()));
        entity.setMonitorType(DictResolver.getItemValueByLabel(PollModDictEnum.site_type, excelDto.getMonitorType()));
        entity.setTownId(DictResolver.getItemValueByLabel(PollModDictEnum.town_info, excelDto.getTownId(), Long::valueOf));
        entity.setAqiFlag(DictResolver.getItemValueByLabel(SysDictEnum.yes_no_type, excelDto.getAqiFlag(), Boolean::valueOf));
        entity.setIsOnline(DictResolver.getItemValueByLabel(SysDictEnum.yes_no_type, excelDto.getIsOnline(), Boolean::valueOf));
        entity.setDischargeLaw(DictResolver.getItemValueByLabel(PollSysDictEnum.discharge_law, excelDto.getDischargeLaw(), Integer::valueOf));
        entity.setDischargeType(DictResolver.getItemValueByLabel(PollSysDictEnum.discharge_type, excelDto.getDischargeType(), Integer::valueOf));
        // 企业可以为空
        if (StringUtils.isNotBlank(excelDto.getOrgId())) {
            entity.setOrgId(DictResolver.getItemValueByLabel(AdminDictEnum.sys_company, excelDto.getOrgId(), Long::valueOf));
        }
        return entity;
    }

    @Override
    public PollMonitorSiteExcelVO exportEntityToVo(PollMonitorSiteEntity entity) {
        PollMonitorSiteExcelVO excelVO = new PollMonitorSiteExcelVO();
        BeanUtils.copyProperties(entity, excelVO);
        excelVO.setTownId(entity.getTownIdStr());
        excelVO.setIsUse(entity.getIsUseStr());
        excelVO.setIsOnline(entity.getIsOnlineStr());
        excelVO.setOrgId(entity.getOrgIdStr());
        excelVO.setAqiFlag(entity.getAqiFlagStr());
        excelVO.setMonitorType(entity.getMonitorTypeStr());
        excelVO.setDischargeLaw(entity.getDischargeLawStr());
        excelVO.setDischargeType(entity.getDischargeTypeStr());
        return excelVO;
    }

    @Override
    public List<GisAir> queryGisAirMonitor(List<Integer> monitorType, String isAqiMonitor, Integer dataType) {
        return baseMapper.queryGisAirMonitor(monitorType, isAqiMonitor, dataType);
    }

    @Override
    public List<GisWater> queryGisWaterMonitor(List<Integer> monitorType, Integer dataType) {
        return baseMapper.queryGisWaterMonitor(monitorType, dataType);
    }

    @Override
    public List<PollMonitorSiteByMainTypeVO> getPollMonitorSiteListByMainType(Integer mainType) {
        List<PollMonitorSiteByMainTypeVO> result = new ArrayList<>();

        List<Integer> monitorTypeByMain = pollMonitorTypeService.getMonitorTypeByMain(mainType);
        for (Integer type : monitorTypeByMain) {
            LambdaQueryWrapper<PollMonitorSiteEntity> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(PollMonitorSiteEntity::getMonitorType, type);
            wrapper.orderByAsc(Arrays.asList(PollMonitorSiteEntity::getSortNum));
            wrapper.eq(PollMonitorSiteEntity::getIsUse, true);
            List<PollMonitorSiteEntity> list = this.list(wrapper);

            LambdaQueryWrapper<PollMonitorTypeEntity> typeWrapper = new LambdaQueryWrapper<>();
            typeWrapper.eq(PollMonitorTypeEntity::getMonitorType, type);
            PollMonitorTypeEntity monitorType = pollMonitorTypeService.getOne(typeWrapper);

            PollMonitorSiteByMainTypeVO vo = new PollMonitorSiteByMainTypeVO();
            BeanUtils.copyProperties(monitorType, vo);
            vo.setMonitorSiteList(list);
            result.add(vo);
        }

        return result;
    }

    @Override
    public List<Long> getIdByMonitorMainType(Integer monitorTypeMain) {
        List<Integer> typeCodeList = pollMonitorTypeService.getMonitorTypeByMain(monitorTypeMain);
        List<PollMonitorSiteEntity> list = this.lambdaQuery().in(PollMonitorSiteEntity::getMonitorType, typeCodeList)
                .select(PollMonitorSiteEntity::getId)
                .list();
        return list.stream().map(PollMonitorSiteEntity::getId).collect(Collectors.toList());
    }
}
